package com.game.centerserver.service.impl;

import com.game.centerserver.infrastructure.entity.Archive;
import com.game.centerserver.infrastructure.entity.ErrorMessage;
import com.game.centerserver.infrastructure.entity.ServerMasterInfo;
import com.game.centerserver.infrastructure.service.ArchiveService;
import com.game.centerserver.infrastructure.service.ErrorMessageService;
import com.game.centerserver.infrastructure.service.ServerMasterInfoService;
import com.game.centerserver.pojo.result.DSTServerProcessView;
import com.game.centerserver.pojo.result.HttpStateCodeResult;
import com.game.centerserver.service.GenerateArchive;
import com.game.centerserver.service.HttpClientService;
import com.game.centerserver.service.ScheduleService;
import com.game.centerserver.utils.BaseUtil;
import jakarta.annotation.Resource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.*;

@Service
public class ScheduleServiceImpl implements ScheduleService {
    @Resource
    ServerMasterInfoService serverMasterInfoService;

    @Resource
    HttpClientService httpClientService;

    @Resource
    ErrorMessageService errorMessageService;

    @Resource
    ArchiveService archiveService;

    @Resource
    GenerateArchive generateArchive;

    @Scheduled(fixedRate = 60 * 1000L)
    @Override
    public void pollMasters() {
        List<ServerMasterInfo> masters = serverMasterInfoService.query(new ServerMasterInfo());

        for (ServerMasterInfo master: masters)
        {
            HttpStateCodeResult<List<DSTServerProcessView>> res = httpClientService.getServerStateList(
                    master.getIp(),
                    master.getPort()
            );

            if (res.getCode() != 200) {
                ErrorMessage errorMessage = new ErrorMessage();
                errorMessage.setErrorTime(new Date());
                errorMessage.setMasterIp(master.getIp());
                errorMessage.setMessage("轮询时错误: " + res.getMessage());
                continue;
            }

            List<DSTServerProcessView> dstServerProcessViewList = res.getPayload();
            for (DSTServerProcessView dstview: dstServerProcessViewList) {
                System.out.println("轮询：" + dstview);
            }

            // 若玩家离线超过24h，关闭进程服务并召回存档
            List<String> archiveIds = getArchiveIdNoPlayersMoreThanMins(
                    dstServerProcessViewList, 24 * 60L);
            if (archiveIds.size() > 0) {
                for (String id: archiveIds) {
                    HttpStateCodeResult<String> httpres = httpClientService.shutdownServer(
                            master.getIp(),
                            master.getPort(),
                            id
                    );
                    if (httpres.getCode() != 200) {
                        System.err.println(id + "shutdown fail");
                        continue;
                    }

                    if (callBackArchiveByIdAndSave(
                            master.getIp(),
                            master.getPort(),
                            id
                    )) {
                        System.out.println(id + "call back sucess");
                    } else {
                        System.err.println(id + "call back fail");
                    }
                }
            }
        }
    }

    // 获取dstServerProcessViewList中运行中的且无玩家时间超时的ArchiveId
    public List<String> getArchiveIdNoPlayersMoreThanMins(
            List<DSTServerProcessView> dstServerProcessViewList,
            Long mins) {
        List<String> res = new ArrayList<>();
        Set<String> archiveset = new HashSet<>();
        for (DSTServerProcessView dstv: dstServerProcessViewList) {
            if (dstv.getState()==1 && dstv.getMinutesNoPlayer() >= mins) {
                archiveset.add(dstv.getArchiveID());
            }
        }

        for(String archiveid: archiveset) {
            res.add(archiveid);
        }

        return res;
    }

    // 召回存档并保存到本地,调用ack
    public boolean callBackArchiveByIdAndSave(
            String IP, Integer port, String archiveId) {
        HttpStateCodeResult<byte[]> httpres = httpClientService.callBackServerByArchiveID(
                IP,
                port,
                archiveId);
        if (httpres.getCode() != 200) {
            System.err.println("IP: " + IP + " callback archive fail");
            return false;
        }

        // 将字节数组写入文件
        File f = Paths.get("temps").resolve(archiveId + "-" + System.currentTimeMillis() + ".tar.gz").toFile();
        if (f.exists()) {
            f.delete();
        }
        try {
            f.createNewFile();
            String filePath = f.getAbsolutePath();
            FileOutputStream fos = new FileOutputStream(filePath);
            fos.write(httpres.getPayload()); // 写入字节数组
            System.out.println("写入文件成功: " + filePath);
        } catch (IOException e) {
            e.printStackTrace(); // 捕获并打印异常
            System.err.println("文件写入失败: " + f.getAbsolutePath());
            return false;
        }

        // TODO 校验所下载文件

        HttpStateCodeResult<String> ackres = httpClientService.ackCallBackServer(IP,
                port,
                archiveId);
        if (ackres.getCode() != 200) {
            System.err.println(archiveId + " : ack send fail");
            return false;
        }

        // 解压缩
        Archive archive = archiveService.queryById(archiveId);
        if (archive == null) {
            return false;
        }
        File targetDir = Paths.get(archive.getLocalPath()).toFile();
        BaseUtil.deleteDirectory(targetDir);
        BaseUtil.extractTarGz(f, Paths.get(archive.getLocalPath()).getParent().toFile());

        if (!generateArchive.clearConfigFile(targetDir)) {
            System.err.println(archiveId + "存档清除配置失败");
            return false;
        }

        archive.setRemote(0);
        archive.setMasterIp(null);
        archiveService.update(archive);

        // TODO 占用端口关闭

        return true;
    }
}
